package audiobeatz;

import audiobeatz.AudioBeatz;
import java.util.ArrayList;
import java.util.Observable;

import audio.WavePlayer;

public class TMetronome extends Observable implements Runnable {

    public enum EOffBeats {

	OFF(0), Three(3), Four(4);

	private int count;

	private EOffBeats(int count) {
	    this.count = count;
	}

	public int getCount() {
	    return this.count;
	}
    }

    private final int CLICK_BEAT_TIMEOUT = 5000;
    private final int CLICK_ACCEPT_COUNT = 2;

    private int bpm;
    private long lastBeat = 0;
    private int msPerBeat;

    private int beatsPlayed = 0;

    private boolean enabled = false;

    private ArrayList<Long> clickBeats = new ArrayList<>();

    private EOffBeats offBeats;

    public TMetronome(int bpm, EOffBeats offBeats) {
	this.offBeats = offBeats;
	setBPM(bpm);
    }

    @Override
    public void run() {
	while (true) {
	    try {
		if (enabled) {
		    if (System.currentTimeMillis() - lastBeat > msPerBeat) {
			lastBeat = System.currentTimeMillis();
			if (offBeats.getCount() != 0 && beatsPlayed % offBeats.getCount() == 0) {
			    beatsPlayed = 0;
			    new WavePlayer("metronome/offbeat.wav").start();
			} else {
			    new WavePlayer("metronome/beat.wav").start();
			}
			beatsPlayed++;
		    }
		}
		Thread.sleep(1);
	    } catch (InterruptedException e) {
		e.printStackTrace();
	    }
	}
    }

    public int getBPM() {
	return this.bpm;
    }

    public void setBPM(int bpm) {
	if (bpm < 0)
	    throw new IllegalArgumentException("Negative BPMs not allowed");
	else if (bpm == 0) {
	    this.bpm = 0;
	    enabled = false;
	    return;
	} else {
	    this.bpm = bpm;
	    msPerBeat = Math.round(60 * 1000 / bpm);
	}
	setChanged();
	notifyObservers("BPM");
    }

    public void startMetro() {
	if (bpm > 0)
	    this.enabled = true;
	setChanged();
	notifyObservers("BPM");
    }

    public void stopMetro() {
	this.enabled = false;
	setChanged();
	notifyObservers("BPM");
    }

    public boolean isRunning() {
	return this.enabled;
    }

    public void setOffBeats(EOffBeats offBeats) {
	this.offBeats = offBeats;
    }

    public void addClickBeat() {
	// If there's data and received key is 5 seconds after last, clear it
	// all and assume user wants to start a new
	if (!clickBeats.isEmpty() && (clickBeats.get(clickBeats.size() - 1) - System.currentTimeMillis() > CLICK_BEAT_TIMEOUT))
	    clickBeats.clear();

	// add the click to the list
	clickBeats.add(System.currentTimeMillis());

	// if we have enough consecutive clicks, calculate average delay and use
	// it
	if (clickBeats.size() >= CLICK_ACCEPT_COUNT) {
	    int delay = 0;
	    for (int i = 0; i < clickBeats.size() - 1; i++) {
		delay += clickBeats.get(i + 1) - clickBeats.get(i);
	    }

	    int avgDelayms = Math.round(delay / (clickBeats.size() - 1));
	    bpm = 60 * 1000 / avgDelayms;
	    setBPM(bpm);
	    // clickBeats.clear();
	    // startMetro();
	    AudioBeatz.setSlider(bpm);
	    setChanged();
	    notifyObservers("BPM");
	}
    }
}